//
// Created by ta&to on 2022/9/28.
//
#include "http_multi_io.h"
#ifdef SOCKET_SERVER_EPOLL
static inline void _init_epoll(struct event_container_s *ev_container){
    initPipe(ev_container->pipefd);
    ev_container->current_read_link = 0;
    ev_container->current_write_link = 0;
    ev_container->epoll_use_fds = (struct event_use_fds_s *) http_malloc(ev_container->max_link * sizeof(struct event_use_fds_s));
    ev_container->epoll_ev_fds = (struct epoll_event *) http_malloc(ev_container->max_link * sizeof(struct epoll_event));
    ev_container->event_read_fds = (struct event_fds_s*) http_calloc(ev_container->max_link * sizeof(struct event_fds_s));
    ev_container->event_write_fds = (struct event_fds_s*) http_calloc(ev_container->max_link * sizeof(struct event_fds_s));
}
static inline void _create_epoll(struct event_container_s *ev_container) {
    /************************
    返回一个epoll文件描述符，size表示epoll能监听的最大连接数
    从linux2.6.8开始，size参数被忽略，必须大于零
    在最初中，是提示内核，给其分配多大的内存，当然，在初始中，如果超过了，内核也将分配更多的空间
    现在，这个提示不再需要，内核会动态的分配空间，根据需要调整数据结构的大小
    但是大小必须大于0,以便于在较旧的内核上运行时，向后兼容。
    *************************/
    ev_container->epoll_fd = epoll_create(1);
    if(ev_container->epoll_fd < 0)
    {
        error(ev_container->logs, "epoll create error[%s]", strerror(errno));
        exit(EXIT_FAILURE);
    }
    for (int i = 0; i < ev_container->max_link;
         ev_container->epoll_use_fds[i].fd = 0,ev_container->epoll_use_fds[i].rd = 0,ev_container->epoll_use_fds[i].wd = 0,
         ev_container->event_read_fds[i].fd = -1, ev_container->event_read_fds[i].events = 0,
         ev_container->event_write_fds[i].fd = -1, ev_container->event_write_fds[i].events = 0, ++i);
    add_event(ev_container, ev_container->pipefd[0], IO_ADD_RD_EVENT, pipe_read_fd_handler, NULL, -1);
}
static inline int _add_event(struct event_container_s *ev_container, int fd, uint32_t event, fd_callback_handler_func fd_handler, void* args, int n) {
    /*********************
    epoll注册函数，select是在监听事件时告诉内核要监听什么类型的事件，这里是先注册要监听的事件类型
    epfd：epoll_create(int size)函数的返回值，
    op: 表示动作，添加删除修改
        EPOLL_CTL_ADD：注册新的fd到epfd中；
        EPOLL_CTL_MOD：修改已经注册的fd的监听事件；
        EPOLL_CTL_DEL：从epfd中删除一个fd；
    fd: 要监听的连接描述符，注册fd到epfd这个池子中
    typedef union epoll_data {
        void *ptr;
        int fd;
        __uint32_t u32;
        __uint64_t u64;
    } epoll_data_t;
    struct epoll_event {
        __uint32_t events; // Epoll events 要监听的事件类型
        epoll_data_t data; // User data variable 在这里，我们使用联合体中的fd，表示要监听连接描述符
    };
    events描述要监听的事件类型，可以是以下的组合
    EPOLLIN ：表示对应的文件描述符可以读（包括对端SOCKET正常关闭）；
    EPOLLOUT：表示对应的文件描述符可以写；
    EPOLLPRI：表示对应的文件描述符有紧急的数据可读（这里应该表示有带外数据到来）；
    EPOLLERR：表示对应的文件描述符发生错误；
    EPOLLHUP：表示对应的文件描述符被挂断；
    EPOLLET： 将EPOLL设为边缘触发(Edge Triggered)模式，这是相对于水平触发(Level Triggered)来说的。
    EPOLLONESHOT：只监听一次事件，当监听完这次事件之后，如果还需要继续监听这个socket的话，需要再次把这个socket加入到EPOLL队列里
    *********************/
    char flag = 0x00;
    struct event_use_fds_s *epoll_use_fds = NULL;
    for(int i = 0; i < ev_container->max_link; ++i) {
        if(ev_container->epoll_use_fds[i].fd == fd) {
            epoll_use_fds = &ev_container->epoll_use_fds[i];
            break;
        }
    }
    if(epoll_use_fds == NULL) {
        for(int i = 0; i < ev_container->max_link; ++i) {
            if(ev_container->epoll_use_fds[i].fd == 0) {
                epoll_use_fds = &ev_container->epoll_use_fds[i];
                epoll_use_fds->fd = fd;
                epoll_use_fds->rd = 0;
                epoll_use_fds->wd = 0;
                break;
            }
        }
    }
    for(int i = 0; i < ev_container->max_link; ++i)
    {
        if (!(flag & 0x01) && !(event & IO_ADD_RD_EVENT ^ IO_ADD_RD_EVENT)) {
            if (ev_container->event_read_fds[i].fd == -1){
                epoll_use_fds->rd = 1;
                flag |= 0x01;
                ev_container->event_read_fds[i].fd = fd;
                ev_container->event_read_fds[i].events = IO_ADD_RD_EVENT;
                ev_container->event_read_fds[i].fd_handler = fd_handler;
                ev_container->event_read_fds[i].args = args;
                ev_container->event_read_fds[i].invoke_num = n;
                atomicIncr(ev_container->current_read_link, 1);
            }
        }
        if (!(flag & 0x10) && !(event & IO_ADD_WT_EVENT ^ IO_ADD_WT_EVENT)) {
            if (ev_container->event_write_fds[i].fd == -1){
                epoll_use_fds->wd = 1;
                flag |= 0x10;
                ev_container->event_write_fds[i].fd = fd;
                ev_container->event_write_fds[i].events = IO_ADD_WT_EVENT;
                ev_container->event_write_fds[i].fd_handler = fd_handler;
                ev_container->event_write_fds[i].args = args;
                ev_container->event_write_fds[i].invoke_num = n;
                atomicIncr(ev_container->current_write_link, 1);
            }
        }
    }
    return 0;
}
static inline int _del_event(struct event_container_s *ev_container, int fd, uint32_t event)
{
    struct event_use_fds_s *epoll_use_fds = NULL;
    for(int i = 0; i < ev_container->max_link; ++i) {
        if(ev_container->epoll_use_fds[i].fd == fd) {
            epoll_use_fds = &ev_container->epoll_use_fds[i];
            break;
        }
    }
    for(int i = 0; i < ev_container->max_link; ++i)
    {
        if(ev_container->event_read_fds[i].fd == fd && !(event & IO_ADD_RD_EVENT ^ IO_ADD_RD_EVENT))
        {
            epoll_use_fds->rd = 0;
            ev_container->event_read_fds[i].fd = -1;
            ev_container->event_read_fds[i].events = 0;
            atomicDecr(ev_container->current_read_link, 1);
        }
        if(ev_container->event_write_fds[i].fd == fd && !(event & IO_ADD_WT_EVENT ^ IO_ADD_WT_EVENT))
        {
            epoll_use_fds->wd = 0;
            ev_container->event_write_fds[i].fd = -1;
            ev_container->event_write_fds[i].events = 0;
            atomicDecr(ev_container->current_write_link, 1);
        }
    }
    return fd;
}

static inline void _reload_epoll(struct event_container_s *ev_container) {
    for(int i = 0; i < ev_container->max_link; i++) {
        if(ev_container->epoll_use_fds[i].fd != 0 && ev_container->epoll_use_fds[i].rd == 0 && ev_container->epoll_use_fds[i].wd == 0) {
            if(epoll_ctl(ev_container->epoll_fd, EPOLL_CTL_DEL, ev_container->epoll_use_fds[i].fd, NULL) == -1)
                error(ev_container->logs, "%d epoll_ctl del error:[%s]", ev_container->epoll_use_fds[i].fd, strerror(errno));
            ev_container->epoll_use_fds[i].fd = 0;
            continue;
        }
        int fd = ev_container->epoll_use_fds[i].fd;
        if(fd != 0) {
            uint32_t events = 0;
            for (int j = 0;j < ev_container->max_link; ++j) {
                if(ev_container->event_read_fds[j].fd == fd)
                    events |= ev_container->event_read_fds[j].events;
                if(ev_container->event_write_fds[j].fd == fd)
                    events |= ev_container->event_write_fds[j].events;
            }

            if(epoll_ctl(ev_container->epoll_fd, EPOLL_CTL_DEL, fd, NULL) == -1)
                error(ev_container->logs, "%d epoll_ctl del error:[%s]",fd, strerror(errno));
            if(events && epoll_ctl(ev_container->epoll_fd, EPOLL_CTL_ADD, fd, &events) == -1)
                error(ev_container->logs, "%d epoll_ctl add error:[%s]", __LINE__, strerror(errno));
        }
    }
}
static inline void _epoll_poll(struct event_container_s *ev_container) {
    /*********************
    epfd： 等待的监听描述符，也就是哪个池子中的内容
    *events： 指针，指向epoll_event的数组，监听描述符中的连接描述符就绪后，将会依次将信息填入
    maxevents： 最多返回最大事件
    timeout： 等待时间，要是有连接描述符就绪，立马返回，如果没有，timeout时间后也返回，单位是ms
    epoll_wait()将会一直阻塞直到：epfd产生事件 / 被信号处理函数打断 / 超时。
    *********************/
    while (1) {
        if(ev_container->status == 1) {
            free_event_io(ev_container);
            closePipe(ev_container->pipefd[0]);
            closePipe(ev_container->pipefd[1]);
            return;
        }
        _reload_epoll(ev_container);                        // 重构数据
        ev_container->epoll_ev_fds = (struct epoll_event *) http_malloc(ev_container->max_link * sizeof(struct epoll_event));
        int wait = epoll_wait(ev_container->epoll_fd, ev_container->epoll_ev_fds, ev_container->max_link,
                              (ISNOTNULL(ev_container->tvp) ? (ev_container->tvp->tv_sec * 1000 +
                                                               (ev_container->tvp->tv_usec + 999) / 1000)
                                                            : -1));//一段超时事件内等待一组文件描述符上的事件
        if (wait == -1) {
            if (errno == EINTR)
                continue;
            error("epoll_wait error [%s]", strerror(errno));
            continue;
        } else if (wait == 0) {
            warn(ev_container->logs, "已超时\n");
            continue;
        } else {
            event_p_t e_args;
            for (int j = 0; j < wait; ++j) {
                struct epoll_event ev = ev_container->epoll_ev_fds[j];
                if (ev.data.fd == -1)
                    continue;
                for (int i = 0; i < ev_container->max_link; ++i) {
                    if (ev_container->event_read_fds[i].fd != ev.data.fd && ev_container->event_write_fds[i].fd != ev.data.fd)
                        continue;
                    if (ev_container->event_read_fds[i].fd == ev.data.fd && ev.events & IO_ADD_RD_EVENT) {
                        if (ev_container->event_read_fds[i].invoke_num > 0) {
                            ev_container->event_read_fds[i].invoke_num--;
                        }
                        e_args.fd = ev_container->event_read_fds[i].fd;
                        e_args.args = ev_container->event_read_fds[i].args;
                        fd_callback_handler_func fd_handler = ev_container->event_read_fds[i].fd_handler;
                        if (ev_container->event_read_fds[i].invoke_num == 0) {
                            _del_event(ev_container, ev_container->event_read_fds[i].fd, IO_ADD_RD_EVENT);
                        }
                        fd_handler(ev_container, &e_args);
                    }
                    if (ev_container->event_write_fds[i].fd == ev.data.fd && ev.events & IO_ADD_WT_EVENT) {
                        if (ev_container->event_write_fds[i].invoke_num > 0) {
                            ev_container->event_write_fds[i].invoke_num--;
                        }
                        e_args.fd = ev_container->event_write_fds[i].fd;
                        e_args.args = ev_container->event_write_fds[i].args;
                        fd_callback_handler_func fd_handler = ev_container->event_write_fds[i].fd_handler;
                        if (ev_container->event_write_fds[i].invoke_num == 0) {
                            _del_event(ev_container, ev_container->event_write_fds[i].fd, IO_ADD_WT_EVENT);
                        }
                        fd_handler(ev_container, &e_args);
                    }
                }
            }
        }
    }
}

void _free_epoll(struct event_container_s *ev_container) {
    if(ev_container->epoll_ev_fds) {
        http_free(ev_container->epoll_ev_fds);
    }
    if(ev_container->event_read_fds) {
        http_free(ev_container->event_read_fds);
    }
    if(ev_container->event_write_fds) {
        http_free(ev_container->event_write_fds);
    }
    close(ev_container->epoll_fd);
}

static inline void _triggerIo(struct event_container_s *ev_container){
    write(ev_container->pipefd[1], "", 1);
}

void init_event_io(struct event_container_s *ev_container){
    _init_epoll(ev_container);
}
void create_event_io(struct event_container_s *ev_container){
    _create_epoll(ev_container);
}
int add_event(struct event_container_s *ev_container, int fd, uint32_t event, fd_callback_handler_func fd_handler, void *args, int n) {
    return _add_event(ev_container, fd, event, fd_handler, args, n);
}
int del_event(struct event_container_s *ev_container, int fd, uint32_t event) {
    return _del_event(ev_container, fd, event);
}
int io_cycle(struct event_container_s *ev_container){
    _epoll_poll(ev_container);
}
void triggerIo(struct event_container_s *ev_container){
    _triggerIo(ev_container);
}
void free_event_io(struct event_container_s *ev_container) {
    _free_epoll(ev_container);
}
#endif